; Example Source Skeleton written by tyro@deco.franken.de ; ; "I worked damn long to add all those comments, so PLEASE leave this ; note in when spreading the file or putting a copy on your own page..." .include "sfr.i" ; To enable the assembler to recognize all Special Function Registers and ; PSW bits, you need the file sfr.i, which should be placed in the same ; directory where the assembler is. * ------------------------------------------------------ * Variable-Definitions ; Here you can define variables to store data. For example, if you want ; to store a counter value somewhere, you can define a variable named ; 'counter' with the following code: ; ; counter = $30 ; ; $30 being the memory address where the variable is stored ; ; Now you can write to that address by using the assigned name, for example ; ; MOV #$00,COUNTER ; ; will store value $00 at memory address $30. Or ; ; LD COUNTER ; ; will load the value stored in memory address $30 into the ACC register. * ------------------------------------------------------ * Reset- and Interrupt-Vectors ; The following are interrupt vectors used by the VMU hardware. DO NOT ; CHANGE ANY OF THEM. ; ; .org
means that the code following the .org directive starts ; at the given address. For example ; ; .org 200 ; ; XOR ACC ; ; means that 'XOR ACC' will be placed at memory offset $200 in the program, .org 0 jmpf start ; far jump to the start of the main program .org $3 jmp nop_irq .org $b jmp nop_irq .org $13 jmp nop_irq .org $1b jmp t1int .org $23 jmp nop_irq .org $2b jmp nop_irq .org $33 jmp nop_irq .org $3b jmp nop_irq .org $43 jmp nop_irq .org $4b clr1 p3int,0 clr1 p3int,1 nop_irq: reti .org $130 t1int: push ie clr1 ie,7 not1 ext,0 jmpf t1int pop ie reti .org $1f0 goodbye: not1 ext,0 jmpf goodbye ; leave game mode * ------------------------------------------------------ * Game Header ; This is the game header, containing info about the game. ; 16 byte Game Description, 32 byte Copyright Info. ; ; The directive .byte means that the following is data that should ; be stored into memory. Examples: ; ; .byte $0c a byte with value $0c (12 decimal) ; .byte 'a' a byte with value $61 (ASCII 'a') ; .byte %00001001 a byte with value $09 (binary notation) .org $200 .byte "Test Skeleton " ; 16 byte .byte "written by tyro@deco.franken.de " ; 32 byte * ------------------------------------------------------ * Game Icon Definition ; Here is defined how many game icons (which are shown in the Dreamcast ; File Manager) are stored in the header, and which what animation speed ; they are displayed. ; ; .word is the same as .byte, only that it's 16 bit long instead of 8 bit. .org $240 .word 1 ; number of icons (max = 3) .word 10 ; animation speed * ------------------------------------------------------ * Game Icon Palette Table ; Here you can define up to 16 different colors for the game icons. ; ; Each color is 4 * 4 bit: Alpha-Contrast / Red / Green / Blue ; ; Example colors: ; ; 1111 0000 0000 0000 $ f000 black ; ; 1111 1111 1100 1100 $ ffcc lt. red ; 1111 1010 0000 0000 $ fa00 dark red ; 1111 1111 0000 0000 $ ff00 red ; ; 1111 1100 1111 1100 $ fcfc lt. green ; 1111 0000 1010 0000 $ f0a0 dark green ; 1111 0000 1111 0000 $ f0f0 green ; ; 1111 1100 1100 1111 $ fccf lt. blue ; 1111 0000 0000 1010 $ f00a dark blue ; 1111 0000 0000 1111 $ f00f blue ; ; 1111 1111 1111 1000 $ fff8 lt. yellow ; 1111 1101 1101 0000 $ fdd0 dark yellow ; 1111 1111 1111 0000 $ fff0 yellow ; ; 1111 1000 1111 1111 $ f8ff lt. cyan ; 1111 0000 1010 1010 $ f0aa dark cyan ; 1111 0010 1111 1111 $ f2ff cyan ; ; 1111 1111 1000 1111 $ ff8f lt. purple ; 1111 1100 0000 1100 $ fc0c dark purple ; 1111 1111 0001 1111 $ ff1f purple ; ; 1111 1111 1111 1111 $ ffff white .org $260 .word $f000, $ff00, $f0f0, $f00f, $fff0, $faff, $ff1f, $ffff ; black, red, green, blue, yellow, cyan, purple, white .word $ffff, $ffff, $ffff, $ffff, $ffff, $ffff, $ffff, $ffff ; white, white, white, white, white, white, white, white * ------------------------------------------------------ * Game Icon Data ; This is where the game icons are placed. Each icon is 512 byte long, ; 32 * 32 pixel, each pixel being represented by 4 bit (one nybble) that ; serve as a pointer into the color palette table. ; ; This example image just shows a yellow background (all nybbles pointing ; to color 4 in the color palette table). .org $280 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 .byte $44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44,$44 * ------------------------------------------------------ * Main Program ; the following code is mandatory to make the game work - just leave ; it 'as is'. start: mov #$a1,ocr ; write $a1 into Oscillation Control Register (OCR) mov #$09,mcr ; write $09 into Mode Control Register (MCR) mov #$80,vccr ; write $80 into LCD Contrast Control Register (VCCR) clr1 p3int,0 ; clear bit 0 in Port 3 Interrupt Control Register ; (P3INT) clr1 p1,7 ; clear bit 7 in Port 1 Latch (P1) mov #$ff,p3 ; write $ff in Port 3 Latch (P3) ; Your own program code starts here. ;) ; ; The following stuff only serves as an example to demonstrate how to ; handle the LCD screen and keypresses. Modify it as you please. call clrscr ; this clears the LCD screen ; TRL and TRH are special function registers used to hold the high byte (TRH) ; and low byte (TRL) of an address. The 16-bit address in those registers, ; together with the value in the ACC register, form an indirect memory address. ; When the LDC instruction is issued, the byte located at (TRH/TRL + ACC) is ; loaded into ACC. mov #screen_data,trh call setscr ; this copies an image to the LCD screen ; The GETKEYS function (taken from Marcus Comstedt's "Tetris" program) ; doesn't really need any explanation, simply use it 'as is', like everyone ; else does. ;) .keypress: call getkeys bn acc,4,.keypress ; branch if button A is pressed bn acc,5,.keypress ; branch if button B is pressed ; br .keypress ; branch even if neither A nor B are pressed ; The following are subroutines. * ------------------------------------------------------ * Clear Screen clrscr: clr1 ocr,5 ; push acc push xbnk push 2 ; mov #0,xbnk .cbank: mov #$80,2 .cloop: mov #0,@R2 ; inc 2 ; ld 2 and #$f bne #$c,.cskip ; ld 2 add #4 st 2 .cskip: ld 2 bnz .cloop ; bp xbnk,0,.cexit ; mov #1,xbnk br .cbank .cexit: pop 2 pop xbnk pop acc set1 ocr,5 ret ; Explanation: ; ; clr1 ocr,5 ; ; The clr1 instruction clears the fifth bit in the Oscillation Control ; Register. The previous hexadecimal value was A1, which is the binary ; number 10100001. The CPU recognizes the right most significant bit as ; the zeroeth bit; the new number in the OCR is 10000001, or 81 in ; hexadecimal. This new value changes the clock speed to 5 Mhz, the ; appropriate speed for accessing the LCD Frame Buffer of the VMU. ; ; push acc ; push xbnk ; push 2 ; ; The values in the accumulator, the bank address register, and memory ; address 002 are stored on the stack. ; ; mov #0,xbnk ; ; Placing the value 0 into the Bank Address Register sets it to zero. The ; LCD Frame Buffer is only large enough for half of the LCD screen; bank zero ; lets us work with the top half of the LCD screen. ; ; .cbank: ; mov #$80,2 ; ; The LCD frame buffer starts at address 0180. So the starting value $80 is ; placed into memory address 2. ; ; .cloop: ; mov #0,@R2 ; ; @R2 is used for indirect addressing and serves as a pointer into the Special ; Function Register Area of the VMU (of which locations $80 - $fb represent ; the LCD Frame Buffer (XRAM) - @R0 and @R1 always point to the General ; Purpose RAM (from 0000 - 00ff), while @R2 and @R3 always point to the ; Special Function Register area (from 0100 - 01ff). ; ; During the first pass, memory address two holds the value $80. Therefore, ; 00000000 is placed into memory address 0180. We've now cleared the first 8 ; pixels on the LCD screen. ; ; inc 2 ; ; We increment the value of memory address 002. During the first pass, this ; would change it to $81. ; ; ld 2 ; ; Now we put the value of memory address 002 into the accumulator. Again, in ; the first pass this is $81. ; ; and #$f ; ; The CPU performs a bitwise AND with the value in the accumulator and $0F. ; During the first pass, we're comparing 10000001 and 00001111; the new value ; is 00000001. ; ; bne #$c,.cskip ; ; This instruction is a conditional statement (an "if"). If the value of the ; accumulator is equal to $0C (12 decimal), goto (branch to) subfunction ; ".cskip". We do this to see if the LCD frame buffer address is divisable ; by 12. Each six bytes (6 * 8 bits) in the LCD Frame Buffer represent one ; line on the LDC screen (48 pixel), and after every two lines there is a ; space of 4 unused bytes. ; ; ld 2 ; add #4 ; st 2 ; ; If we have written two full lines to the the LCD Frame Buffer (of which the ; current write address is represented in memory address 002), then we add 4, ; to skip the unused bytes and set the pointer to the beginning of the next ; line. During the first pass it isn't, so this part is skipped. ; ; .cskip: ; ld 2 ; bnz .cloop ; ; This is a check to see if we're finished with the LCD Frame Buffer in ; bank 0 (half of the screen). If we've reached the end and roll over to 00, ; go back to ".cloop". ; ; bp xbnk,0,.cexit ; ; If the Bank Address Register does not equal zero (meaning that we have ; already written to the LCD Frame Buffer in both banks), goto ".cexit". ; ; mov #1,xbnk ; br .cbank ; ; Move 1 into the Bank Address Register (switch to bank 1). This lets us ; clear the bottom half of the screen. We then continue with writing to the ; lower half of the screen just like we previously wrote to the upper half ; (because all that's different is that the lower half is in another bank). ; ; .cexit: ; pop 2 ; pop xbnk ; pop acc ; set1 ocr,5 ; ret ; ; This restores the values of memory address 2, the Bank Address Register, ; and the Accumulator. The clock speed is reset to A1 and the function ; returns to the address following the code that called it. * ------------------------------------------------------ * Set Screen setscr: clr1 ocr,5 push acc push xbnk ; push c push 2 mov #$80,2 ; xor acc st xbnk st c .sloop: ldc st @R2 ; inc 2 ; ld 2 and #$f bne #$c,.sskip ; ld 2 add #4 st 2 ; bnz .sskip ; inc xbnk mov #$80,2 .sskip: inc c ld c bne #$c0,.sloop ; pop 2 pop c pop xbnk pop acc set1 ocr,5 ret ; Explanation: ; ; clr1 ocr,5 ; push acc ; push xbnk ; ; Setscr is similar to clrscr. The C register is used to as a counter for ; the table data (the picture we're going to copy to the LCD screen). ; ; push c ; push 2 ; mov #$80,2 ; ; xor acc ; st xbnk ; st c ; ; By XORing the accumulator with itself, we are setting it to zero. ; ; .sloop: ; ldc ; ; LDC creates a 16-bit address by combining the 16-bit address in TRH/TRL ; with the value in the accumulator. The byte at this address (the beginning ; of our picture data) is then loaded into the accumulator. ; ; In the first pass, ACC contains the value zero, so the value at address ; (TRH/TRL + 0) is loaded. ; ; st @R2 ; ; We then write this data into the LCD Frame Buffer. ; ; inc 2 ; ; ld 2 ; and #$f ; bne #$c,.sskip ; ; ld 2 ; add #4 ; st 2 ; ; bnz .sskip ; ; inc xbnk ; mov #$80,2 ; ; All of the above is similar to clrscr. ; ; .sskip: ; inc c ; ld c ; ; Here we increase C by one, and load the new value into the accumulator. ; ; bne #$c0,.sloop ; ; If the accumulator has not yet reached hexadecimal value C0 (192 decimal, ; 6 bytes per screenline * 32 lines), we branch back to ".sloop". Since the ; accumulator now contains a value that has been increased by one, during this ; pass we will now load the byte from our screen-data that follows the one we ; loaded during our previous pass. ; ; pop 2 ; pop c ; pop xbnk ; pop acc ; set1 ocr,5 ; ret ; ; Again the previously saved values are restored and the function returns to ; the address following the code that called it. screen_data: ; ; 32 lines with 6 bytes (48 pixel) per line ("Hello World" image) ; .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000100,%01001111,%10010000,%00100000,%00111000,%00000000 .byte %00000100,%01001000,%00010000,%00100000,%01000100,%00000000 .byte %00000111,%11001111,%00010000,%00100000,%01000100,%00000000 .byte %00000100,%01001000,%00010000,%00100000,%01000100,%00000000 .byte %00000100,%01001111,%10011111,%00111110,%00111000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000100,%01000111,%00011110,%00100000,%01111000,%11100000 .byte %00000100,%01001000,%10010001,%00100000,%01000100,%11100000 .byte %00000101,%01001000,%10011110,%00100000,%01000100,%01000000 .byte %00000101,%01001000,%10010100,%00100000,%01000100,%00000000 .byte %00000010,%10000111,%00010010,%00111110,%01111000,%01000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 .byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000 * ---------------------------------------------------------------------- * getkeys - loads Port 3 data into ACC * also handles Mode-button (QUIT), Dreamcast Connect and Sleep getkeys: bp p7,0,quit ; Quit, if Dreamcast Connection ld p3 ; read key status bn acc,6,quit ; if Mode Key pressed, Quit bn acc,7,sleep ; if Sleep Key pressed, then Pause ret ; otherwise return with pressed key in ACC quit: jmp goodbye ; Long Jump, in case we are too far away ; for a 'normal' branch sleep: set1 pcon,0 ; activate HALT mode (saves power) bn p3,7,sleep ; wait until Sleep Key is released mov #0,vccr ; turn off LCD ; sleepmore: set1 pcon,0 ; activate HALT mode (saves power) bp p7,0,quit ; Docked? bp p3,7,sleepmore ; no Sleep Key pressed yet mov #$80,vccr ; turn on LCD again ; waitsleepup: set1 pcon,0 ; activate HALT modus (saves power) bn p3,7,waitsleepup br getkeys ; continue to wait for keypress * ---------------------------------------------------------------------- * End .cnop 0,$200 ; pad to an even number of blocks